package com.freeter.modules.gjs.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.freeter.modules.gjs.entity.StockStatus;
import com.freeter.modules.gjs.entity.Transaction;
import com.freeter.modules.gjs.entity.AppRequestRecord;
import com.freeter.modules.gjs.mapper.StockStatusMapper;
import com.freeter.modules.gjs.mapper.TransactionMapper;
import com.freeter.modules.gjs.mapper.AppRequestRecordMapper;
import com.freeter.modules.gjs.service.TransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.Base64;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;

@Service
public class TransactionServiceImpl implements TransactionService {
    @Autowired
    private TransactionMapper transactionMapper;
    @Autowired
    private StockStatusMapper stockStatusMapper;
    @Autowired
    private AppRequestRecordMapper appRequestRecordMapper;

    @Value("${mark.usage.img.folder:/tmp/mark_usage}")
    private String markUsageImgFolder;
    @Value("${calc.ticket.folder:/tmp/calc_ticket}")
    private String calcTicketFolder;

    private String randomFileName() {
        java.util.Calendar currentDate = java.util.Calendar.getInstance();
        int YYYY = currentDate.get(java.util.Calendar.YEAR);
        int MO = currentDate.get(java.util.Calendar.MONTH) + 1;
        int DD = currentDate.get(java.util.Calendar.DATE);
        int hh = currentDate.get(java.util.Calendar.HOUR_OF_DAY);
        int mm = currentDate.get(java.util.Calendar.MINUTE);
        int ss = currentDate.get(java.util.Calendar.SECOND);
        int fff = currentDate.get(java.util.Calendar.MILLISECOND);
        int randomNum = (int)(Math.random() * (999 - 100 + 1) + 100);
        return String.format("%04d%02d%02d%02d%02d%02d%03d%d", YYYY, MO, DD, hh, mm, ss, fff, randomNum);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean adjustStock(Map<String, Object> req) throws Exception {
        // 1. 参数提取
        String id = String.valueOf(req.get("id"));
        Double qty = req.get("qty") == null ? null : Double.valueOf(req.get("qty").toString());
        String remarks = (String) req.get("remarks");
        Long userId = req.get("user_id") == null ? null : Long.valueOf(req.get("user_id").toString());
        // 2. 参数校验
        if (id == null || qty == null || remarks == null) {
            throw new Exception("Missing required field");
        }
        if (qty == 0) throw new Exception("Please input valid QTY");
        // 3. 查询库存
        StockStatus stock = stockStatusMapper.selectById(Long.valueOf(id));
        if (stock == null) throw new Exception("Item not found");
        double stockQty = stock.getStockQty() == null ? 0 : stock.getStockQty();
        if (qty < 0 && stockQty < -qty) throw new Exception("Out of stock");
        // 4. 更新库存
        double newQty = stockQty + qty;
        stock.setStockQty(newQty);
        stockStatusMapper.updateById(stock);
        // 5. 插入流水
        Transaction transaction = new Transaction();
        transaction.setStockId(Long.valueOf(id));
        transaction.setUserId(userId);
        transaction.setQty(new BigDecimal(qty));
        transaction.setRemarks(remarks);
        transaction.setCalcStatus(1);
        transaction.setUponCalc(new BigDecimal(newQty));
        transaction.setSource("Adjust");
        transaction.setRecordTime(new java.util.Date());
        transactionMapper.insert(transaction);
        // 6. 可选：写本地transaction文件（如需100%还原Node.js）
        java.util.Map<String, Object> writeData = new java.util.HashMap<>();
        writeData.put("id", transaction.getId());
        writeData.put("stock_id", id);
        writeData.put("user_id", userId);
        writeData.put("qty", qty);
        writeData.put("remarks", remarks);
        writeData.put("calc_status", 1);
        writeData.put("upon_calc", newQty);
        writeData.put("source", "Adjust");
        String transactionFilePath = calcTicketFolder + File.separator + "transaction" + File.separator + java.util.UUID.randomUUID();
        java.io.File file = new java.io.File(transactionFilePath);
        file.getParentFile().mkdirs();
        java.nio.file.Files.write(java.nio.file.Paths.get(transactionFilePath), new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsBytes(writeData));
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markUsage(Map<String, Object> req) throws Exception {
        // 1. 参数提取
        String projectCode = (String) req.get("project_code");
        String siteId = String.valueOf(req.get("site_id"));
        String id = String.valueOf(req.get("id"));
        Double qty = req.get("qty") == null ? null : Double.valueOf(req.get("qty").toString());
        String pickedBy = (String) req.get("picked_by");
        String remarks = (String) req.get("remarks");
        List<String> photos = (List<String>) req.getOrDefault("photos", new ArrayList<>());
        List<String> videos = (List<String>) req.getOrDefault("videos", new ArrayList<>());
        String requestId = (String) req.get("request_id");
        Long userId = req.get("user_id") == null ? null : Long.valueOf(req.get("user_id").toString());
        String csrfToken = (String) req.get("csrf_token");
        // 2. 幂等校验
        if (requestId != null && userId != null && csrfToken != null) {
            AppRequestRecord exist = appRequestRecordMapper.selectOne(new AppRequestRecord() {{
                setUserId(userId); setCsrfToken(csrfToken); setRequestId(requestId); setActionType("mark-usage");
            }});
            if (exist != null) return true;
        }
        // 3. 参数校验
        if (projectCode == null || siteId == null || id == null || qty == null || pickedBy == null) {
            throw new Exception("Missing required field");
        }
        if (qty <= 0) throw new Exception("Please input valid QTY");
        // 4. 查询库存
        StockStatus stock = stockStatusMapper.selectById(Long.valueOf(id));
        if (stock == null) throw new Exception("Item not found");
        double availableQty = (stock.getStockQty() == null ? 0 : stock.getStockQty()) - (stock.getHoldQty() == null ? 0 : stock.getHoldQty());
        if (qty > availableQty) throw new Exception("Exceed the existing qty " + availableQty);
        // 5. 多批次扣减
        List<Long> trxIds = new ArrayList<>();
        List<Map<String, Object>> writeDataList = new ArrayList<>();
        if (stock.getItemExpDate() != null && !stock.getItemExpDate().isEmpty()) {
            List<StockStatus> batchList = stockStatusMapper.selectByProjectSiteBarcode(projectCode, siteId, Long.valueOf(id));
            double qty2 = qty;
            for (StockStatus batch : batchList) {
                if (qty2 > 0 && batch.getStockQty() > 0) {
                    double deduct = Math.min(batch.getStockQty(), qty2);
                    batch.setStockQty(batch.getStockQty() - deduct);
                    stockStatusMapper.updateById(batch);
                    Transaction transaction = new Transaction();
                    transaction.setStockId(batch.getId());
                    transaction.setUserId(userId);
                    transaction.setQty(new BigDecimal(deduct));
                    transaction.setPickedBy(pickedBy);
                    transaction.setRemarks(remarks);
                    transaction.setSource("Use");
                    transaction.setRecordTime(new java.util.Date());
                    transactionMapper.insert(transaction);
                    trxIds.add(transaction.getId());
                    // 写本地文件数据
                    Map<String, Object> writeData = new java.util.HashMap<>();
                    writeData.put("id", transaction.getId());
                    writeData.put("stock_id", batch.getId());
                    writeData.put("user_id", userId);
                    writeData.put("qty", deduct);
                    writeData.put("picked_by", pickedBy);
                    writeData.put("remarks", remarks);
                    writeData.put("calc_status", 0);
                    writeData.put("source", "Use");
                    writeDataList.add(writeData);
                    qty2 -= deduct;
                }
            }
        } else {
            // 单批次扣减
            stock.setStockQty(stock.getStockQty() - qty);
            stockStatusMapper.updateById(stock);
            Transaction transaction = new Transaction();
            transaction.setStockId(stock.getId());
            transaction.setUserId(userId);
            transaction.setQty(new BigDecimal(qty));
            transaction.setPickedBy(pickedBy);
            transaction.setRemarks(remarks);
            transaction.setSource("Use");
            transaction.setRecordTime(new java.util.Date());
            transactionMapper.insert(transaction);
            trxIds.add(transaction.getId());
            Map<String, Object> writeData = new java.util.HashMap<>();
            writeData.put("id", transaction.getId());
            writeData.put("stock_id", stock.getId());
            writeData.put("user_id", userId);
            writeData.put("qty", qty);
            writeData.put("picked_by", pickedBy);
            writeData.put("remarks", remarks);
            writeData.put("calc_status", 0);
            writeData.put("source", "Use");
            writeDataList.add(writeData);
        }
        // 6. 写入app_request_record
        if (requestId != null && userId != null && csrfToken != null) {
            AppRequestRecord record = new AppRequestRecord();
            record.setUserId(userId);
            record.setCsrfToken(csrfToken);
            record.setRequestId(requestId);
            record.setActionType("mark-usage");
            record.setRecordTime(new java.util.Date());
            appRequestRecordMapper.insert(record);
        }
        // 7. 图片/视频保存
        for (Long insertId : trxIds) {
            String dirPath = markUsageImgFolder + File.separator + projectCode + File.separator + insertId;
            File dir = new File(dirPath);
            if (!dir.exists()) dir.mkdirs();
            for (String photo : photos) {
                if (photo != null && !photo.isEmpty()) {
                    String filename = randomFileName() + ".jpg";
                    File file = new File(dirPath, filename);
                    byte[] buffer = Base64.getDecoder().decode(photo);
                    try (FileOutputStream fos = new FileOutputStream(file)) {
                        fos.write(buffer);
                    }
                }
            }
            for (String video : videos) {
                if (video != null && !video.isEmpty()) {
                    String filename = randomFileName() + ".mp4";
                    File file = new File(dirPath, filename);
                    byte[] buffer = Base64.getDecoder().decode(video);
                    try (FileOutputStream fos = new FileOutputStream(file)) {
                        fos.write(buffer);
                    }
                }
            }
        }
        // 8. 写本地transaction文件
        for (Map<String, Object> writeData : writeDataList) {
            String transactionFilePath = calcTicketFolder + File.separator + "transaction" + File.separator + UUID.randomUUID();
            File file = new File(transactionFilePath);
            file.getParentFile().mkdirs();
            Files.write(Paths.get(transactionFilePath), new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsBytes(writeData));
        }
        return true;
    }
} 